package com.gitee.l0km.beanfilter.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.common.spring.core.annotation.AnnotationAttributes;
import com.gitee.l0km.common.spring.core.annotation.MergedAnnotation;
import com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations;
import static com.google.common.base.Preconditions.checkArgument;

public class DelegateMethodSupport {
	private static final String DELEGATE_METHOD_CLASS = "com.gitee.l0km.codegen.annotations.DelegateMethod";
	/** {@code com.gitee.l0km.codegen.annotations.DelegateMethod}注解字段名定义  */
	private static final String DELEGATE_METHOD_DELEGATE_TYPE = "delegateType";
	private static final String DELEGATE_METHOD_DELEGATE_TYPE_NAME = "delegateTypeName";
	public static final String DELEGATE_METHOD_NAME = "name";
	public static final String DELEGATE_METHOD_PARAMETERTYPES = "parameterTypes";
	/**
	 * 查找方法定义的 {@code com.gitee.l0km.codegen.annotations.DelegateMethod}注解,返回注解属性对象<br>
	 * 如果没有找到返回空的{@link AnnotationAttributes}对象
	 * @param method
	 */
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static AnnotationAttributes delegateMethodAttributesOf(Method method){
		MergedAnnotation<Annotation> annot = 
				TypeMappedAnnotations.from(method).get(DELEGATE_METHOD_CLASS);
		if(!annot.isDirectlyPresent()) {
			return new AnnotationAttributes();
		}
		return annot.asAnnotationAttributes();
	}
	/**
	 * <p>从给定的方法和注解属性中获取委托类型。</p>
	 * <p>如果传入的注解属性对象为空，则会调用 {@link #delegateMethodAttributesOf(Method)} 方法获取该方法的 {@code com.gitee.l0km.codegen.annotations.DelegateMethod} 注解属性。</p>
	 * <p>若注解属性为空，则返回该方法的声明类。</p>
	 * <p>若注解属性中指定了委托类型类，则返回该类；若指定的是委托类型类名，则尝试通过类名加载类并返回。</p>
	 * 
	 * @param method 要检查的方法
	 * @param attrs 方法的 {@code com.gitee.l0km.codegen.annotations.DelegateMethod} 注解属性对象
	 * @return 委托类型的类对象
	 */
	public static Class<?> getDelegateTypeFrom(Method method, AnnotationAttributes attrs){
		if(null == attrs) {
			attrs = delegateMethodAttributesOf(method);
		}
		if(attrs.isEmpty()) {
			return method.getDeclaringClass();
		}
		Class<?> delegateType = attrs.getClass(DELEGATE_METHOD_DELEGATE_TYPE);
		if(!Object.class.equals(delegateType)) {
			return delegateType;
		}
		String delegateTypeName=attrs.getString(DELEGATE_METHOD_DELEGATE_TYPE_NAME);
		checkArgument(!delegateTypeName.isEmpty(),"NOT DEFINED deletage type for @%s on %s",
				DELEGATE_METHOD_CLASS,method);
		try {
			return Class.forName(delegateTypeName);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(String.format("INVALID  deletage type '%s' for @%s on %s",
					delegateTypeName,
					DELEGATE_METHOD_CLASS,method));
		}
	}
	
	/**
	 * @deprecated 方法名拼写错误，已废弃，使用{@link #getDelegateTypeFrom(Method, AnnotationAttributes)}代替
	 */
	public static Class<?> getDeleteTypeFrom(Method method, AnnotationAttributes attrs){
		return getDelegateTypeFrom(method, attrs);
	}
	/**
	 * 如果输入的方法是代理方法，则返回实际委托的方法<br>
	 * 如果方法定义了 {@code com.gitee.l0km.codegen.annotations.DelegateMethod}注解则根据注解定义的参数返回代理方法，
	 * 否则返回 {@code method}本身
	 * @param method
	 */
	public static Method findDelegateMethodOrSelf(Method method){
		AnnotationAttributes attrs = delegateMethodAttributesOf(method);
		if(attrs.isEmpty()) {
			return method;
		}
		Class<?> delegateType = getDeleteTypeFrom(method, attrs);
		String name = attrs.getString(DELEGATE_METHOD_NAME);
		Class<?>[] parameterTypes = attrs.getClassArray(DELEGATE_METHOD_PARAMETERTYPES);
		try {
			return  delegateType.getMethod(name, parameterTypes);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}

}
